package network.ssl;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

import org.hibernate.Session;

import bdd.sql.server.hibernate.HibernateUtil;

import network.ssl.image.KrakenImageThreadServer;

import tools.ConfParser;

/**
 * Serveru TCP.
 * @author Guillaume Poças
 */
public class KrakenSSLServer {
	
	//private static final int DEFAULT_PORT_NUMBER = 18000;
	//local
	//private static final String PATH_CONF_FILE = "C:\\Documents and Settings\\Guillaume\\workspace\\vsm_M2_reseau\\conf\\sslserver.conf";
	//serv web
	//private static final String PATH_CONF_FILE = "C:\\Serveur Java\\sslserver.conf";
	//serv ubuntu
	private static final String PATH_CONF_FILE = "/etc/kraken/sslserver.conf";
	
	private static final String PATH_KEYSTORE = "PATH_KEYSTORE";
	private static final String KEYSTORE_PASSWORD = "KEYSTORE_PASSWORD";
	private static final String DEFAULT_PORT = "DEFAULT_PORT";
	private static final String IMAGE_PORT = "IMAGE_PORT";
	private static final String VIDEO_SERVICE = "VIDEO_SERVICE";
	private static final String VIDEO_PORT = "VIDEO_PORT";
	
	private KrakenImageThreadServer krakenImageThreadServer;
	private ConcurrentHashMap<String, String> streamingServiceCommandMap;
	private ConcurrentHashMap<String, Integer> streamingServiceFPSMap;
	private ArrayList<PrintWriter> tabClients_cmd;
	private ArrayList<PrintWriter> tabClients_vid;
	private int nbClients_cmd;
	private int nbClients_vid;
	private int port;
	private boolean serverOn;
	
	/**
	 * Constructeur.
	 * @param port_
	 */
	public KrakenSSLServer(String port_){
		try{
			tabClients_cmd = new ArrayList<PrintWriter>(); // contiendra tous les flux de sortie vers les clients
			tabClients_vid = new ArrayList<PrintWriter>();
			streamingServiceCommandMap = new ConcurrentHashMap<String, String>();
			streamingServiceFPSMap = new ConcurrentHashMap<String, Integer>();
			nbClients_cmd = 0;
			nbClients_vid = 0;
			serverOn = true;
			
			ConfParser parser = new ConfParser();
			try {
				parser.parse(new File(PATH_CONF_FILE)); //Applet.Constant ? => non car pas d'AppletConstant cote server ! fichier de conf
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			String keystorePath = parser.getValue(PATH_KEYSTORE);
			String keystorePassword = parser.getValue(KEYSTORE_PASSWORD);
			int default_port_number = new Integer(parser.getValue(DEFAULT_PORT));
			String image_port_number = parser.getValue(IMAGE_PORT);
			String video_service = parser.getValue(VIDEO_SERVICE);
			int video_port_number = new Integer(parser.getValue(VIDEO_PORT));
			
			if(port_.equals(""))	port = default_port_number; //port 18000 par défaut
			else	port = new Integer(port_); //sinon premier argument

			//init à partir d'un fichier de conf !
			initSSL(keystorePath, keystorePassword);
			SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
			SSLServerSocket sslserversocket_auth = (SSLServerSocket) sslserversocketfactory.createServerSocket(port);
			printWelcome(port);
			SSLServerSocket sslserversocket_video = (SSLServerSocket) sslserversocketfactory.createServerSocket(video_port_number);
			printWelcome(video_port_number);
			
			Session session = HibernateUtil.getSession();

		    krakenImageThreadServer = new KrakenImageThreadServer(this, image_port_number);
		    while(serverOn){ // attente en boucle de connexion (bloquant sur serverSocket.accept)
		    	new KrakenSSLThread(sslserversocket_auth.accept(),this, port); // un client se connecte, un nouveau thread client est lancé
		    	//System.out.println("Connexion_auth?");
		    	if(video_service.equals("YES")){
		    		new KrakenSSLThread(sslserversocket_video.accept(),this, video_port_number); // un client se connecte, un nouveau thread client est lancé
		    		//System.out.println("Connexion_video?");
		    	}
		    }
	    }
	    catch(Exception e){
	    	e.printStackTrace();//
	    }
	}

	/**
	 * Informations sur le serveur.
	 * @param port
	 */
	public void printWelcome(Integer port){
		System.out.println("--------");
		System.out.println("Kr4ken SSL");
		System.out.println("Projet Kr4ken M2 ISIM SIC 2014");
		System.out.println("Version 0.2 : 19/03/2014");
		System.out.println("--------");
		System.out.println("Demarre sur le port : " +port.toString());
		System.out.println("--------");
	}
	
	public void initSSL(String keyStore, String passKey) {
		System.setProperty("javax.net.ssl.trustStore", keyStore);
		System.setProperty("javax.net.ssl.trustStorePassword", passKey);
		System.setProperty("javax.net.ssl.keyStore", keyStore);
		System.setProperty("javax.net.ssl.keyStorePassword", passKey);
	}

	/**
	 * Envoi un message à tous les clients du serveur.
	 * @param message
	 * @param sLast
	 */
	synchronized public void sendAll(String message,String sLast){
		PrintWriter out;
		
		for(int i = 0; i < tabClients_cmd.size(); i++){ 
			out = (PrintWriter) tabClients_cmd.get(i);
			
			if(out != null){
				out.println(message+sLast);
				out.flush();
			}
		}
	}

	/**
	 * Supprime le client.
	 * @param i
	 */
	synchronized public void delClient(int i, int type){		
		if(type == 0){
			nbClients_cmd--;
			if(tabClients_cmd.get(i) != null){
				tabClients_cmd.remove(i);
			}
		}
		if(type == 1){
			nbClients_vid--;
			if(tabClients_vid.get(i) != null){
				tabClients_vid.remove(i);
			}
		}
	}
	
	public int getPort(){ return this.port; }
	
	public void setServerOnOff(boolean state){ this.serverOn = state; }

	/**
	 * Ajout un nouveau client.
	 * @param out
	 * @return
	 */
	synchronized public int addClient(PrintWriter out, int type){
		if(type == 0){
			nbClients_cmd++;
			tabClients_cmd.add(out);
			return tabClients_cmd.size()-1;
		}
		if(type == 1){
			nbClients_vid++;
			tabClients_vid.add(out);
			return tabClients_vid.size()-1;
		}
		else return -1;
	}

	/**
	 * Le nombre de clients connectés.
	 * @return
	 */
	synchronized public int getNbClientsCmd(){
		return nbClients_cmd;
	}
	
	/**
	 * Le nombre de clients connectés.
	 * @return
	 */
	synchronized public int getNbClientsVid(){
		return nbClients_vid;
	}

	public KrakenImageThreadServer getKrakenImageThreadServer() {	return krakenImageThreadServer;	}
	public void setKrakenImageThreadServer(		KrakenImageThreadServer krakenImageThreadServer) {		this.krakenImageThreadServer = krakenImageThreadServer;	}

	public ConcurrentHashMap<String, String> getStreamingServiceCommandMap() {	return streamingServiceCommandMap;	}
	//public void setStreamingServiceMap(ConcurrentHashMap<String, String> streamingServiceMap) {	this.streamingServiceMap = streamingServiceMap;	}
	
	public void insertCommandIntoMap(String sourceName, String command){
		this.streamingServiceCommandMap.put(sourceName, command);
		//System.out.println("InsertCommandFrom:"+sourceName+"&"+this.streamingServiceCommandMap.containsKey(sourceName)+"&in:"+streamingServiceCommandMap);
	}

	public ConcurrentHashMap<String, Integer> getStreamingServiceFPSMap() {	return streamingServiceFPSMap;	}
	//public void setStreamingServiceFPSMap(ConcurrentHashMap<String, Integer> streamingServiceFPSMap) {		this.streamingServiceFPSMap = streamingServiceFPSMap;	}
	
	public void insertFPSIntoMap(String sourceName, Integer fps){
		this.streamingServiceFPSMap.put(sourceName, fps);
		//System.out.println("InsertFPSFrom:"+sourceName+"&"+this.streamingServiceFPSMap.containsKey(sourceName)+"&in:"+streamingServiceFPSMap);
	}
}
